#ifdef WIN32
#ifndef SPEAKER_SINGLE_H
#define SPEAKER_SINGLE_H
#include <QDebug>
#include <QElapsedTimer>
#include <QMutex>
#include <QObject>
#include <QThread>
#include <sapi.h>
#include <wtypes.h>
class Speaker : public QObject
{
	Q_OBJECT
public:
	static Speaker& instance()
	{
		static Speaker instance;
		return instance;
	}
	void speak(const QString& text)
	{
		if (m_worker && !text.isEmpty()) m_worker->requestSpeak(text);
	}
private:
	explicit Speaker(QObject* parent = nullptr) : QObject(parent)
	{
		m_worker = new WorkerThread(this);
		m_worker->start();
	}
	~Speaker()
	{
		if (m_worker) {
			m_worker->stop();
			m_worker->wait(1000);
			if (m_worker->isRunning()) {
				qWarning() << "Worker thread terminated forcibly";
				m_worker->terminate();
			}
			delete m_worker;
		}
	}
	Speaker(const Speaker&) = delete;
	Speaker& operator=(const Speaker&) = delete;
	class WorkerThread : public QThread
	{
	public:
		explicit WorkerThread(Speaker* parent) : m_parent(parent), m_stopRequested(false) {}
		void requestSpeak(const QString& text)
		{
			QMutexLocker locker(&m_mutex);
			m_text = text;
		}
		void stop()
		{
			QMutexLocker locker(&m_mutex);
			m_stopRequested = true;
		}
	protected:
		void run() override
		{
			HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
			if (FAILED(hr)) return;
			ISpVoice* pVoice = nullptr;
			hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**) &pVoice);
			if (SUCCEEDED(hr)) {
				pVoice->SetRate(0);
				pVoice->SetVolume(100);
				while (true) {
					QString textToSpeak;
					bool stop = false;
					{
						QMutexLocker locker(&m_mutex);
						if (!m_text.isEmpty()) {
							textToSpeak = std::move(m_text);
							m_text.clear();
						}
						stop = m_stopRequested;
					}
					if (stop) break;
					if (!textToSpeak.isEmpty()) {
						const std::wstring wtext = textToSpeak.toStdWString();
						pVoice->Speak(wtext.c_str(), SPF_ASYNC, NULL);
					}
					msleep(100);
				}
				pVoice->Release();
			}
			CoUninitialize();
		}
	private:
		Speaker* m_parent;
		QString m_text;
		QMutex m_mutex;
		bool m_stopRequested;
	};
	WorkerThread* m_worker;
};
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "sapi.lib")
#endif
#endif
